package de.fhkl.whatsstroke;

import java.io.File;
import java.util.UUID;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import de.fhkl.whatsstroke.StrokeService.StrokeBinder;

/**
 * Outbox and status page for videos
 */
public class Outbox extends Activity
{
	/** Constants for the key values used with the shared preferences */
	private static final String STATE = "state";
	private static final String DESCRIPTION = "description";
	private static final String RATING = "rating";
	private static final String FILE_SIZE = "fileSize";
	private static final String PROGRESS = "progress";
	private static final String VIDEO_ID = "videoId";
	private static final String VIDEO_PATH = "videoPath";

	private UUID mVideoId;
	private String mVideoPath;

	/**
	 * Shared preferences are used to persist and retrieve essential data
	 * between two application starts.
	 */
	private SharedPreferences mPreferences;

	// GUI references
	private RelativeLayout mRatingLayout;
	private Button mRecordButton;
	private TextView mTransmissionDescription;
	private ProgressBar mProgressBar;
	private TextView mRatingDescription;
	private TextView mRatingText;
	private Button mAcceptButton;
	private Button mCancelButton;

	/** BroadcastReceiver for broadcast intents from the service */
	private BroadcastReceiver mReceiver;

	/**
	 * ServiceConnection to retrieve a reference for a save way to call methods
	 * from the service
	 */
	private ServiceConnection mConnection;

	/** Reference to the underlying service */
	private StrokeService mService;

	/** Indicates whether the service is bound to this activity */
	private boolean mIsServiceBound;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);

		setContentView(R.layout.activity_outbox);

		mPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());

		mRatingLayout = (RelativeLayout) findViewById(R.id.rating_layout);
		mRecordButton = (Button) findViewById(R.id.new_recording_button);
		mRecordButton.setOnClickListener(new RecordButtonListener());
		mTransmissionDescription = (TextView) findViewById(R.id.text_transmission_discription);
		mProgressBar = (ProgressBar) findViewById(R.id.progress);
		mRatingDescription = (TextView) findViewById(R.id.text_rating_description);
		mRatingText = (TextView) findViewById(R.id.text_rating);
		mAcceptButton = (Button) findViewById(R.id.button_accept);
		mAcceptButton.setOnClickListener(new AcceptRatingButtonListener());
		mCancelButton = (Button) findViewById(R.id.button_cancel);
		mCancelButton.setOnClickListener(new CancelButtonListener());

		restoreView();

		// In most cases this won't do anything. Service should be already
		// running. Let's try to start it just in case it doesn't.
		Intent intent = new Intent(getApplicationContext(), StrokeService.class);
		getApplicationContext().startService(intent);

		mConnection = new StrokeServiceConnection();
		mReceiver = new StrokeBroadcastReceiver();

		mIsServiceBound = false;
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu)
	{
		getMenuInflater().inflate(R.menu.activity_outbox, menu);
		return true;
	}

	@Override
	protected void onStart()
	{
		super.onStart();

		this.bindService(new Intent(this, StrokeService.class), mConnection, 0);

		IntentFilter filter = new IntentFilter();
		filter.addAction("de.fhkl.whatsstroke.VIDEO_TRANSMISSION_PROGRESS");
		filter.addAction("de.fhkl.whatsstroke.VIDEO_TRANSMISSION_COMPLETE");
		filter.addAction("de.fhkl.whatsstroke.UPDATE_VIEW");
		this.registerReceiver(mReceiver, filter);

		restoreView();
	}

	@Override
	protected void onStop()
	{
		if (mIsServiceBound)
		{
			this.unbindService(mConnection);
		}

		this.unregisterReceiver(mReceiver);

		super.onStop();
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item)
	{
		switch (item.getItemId())
		{
			case R.id.menu_close_app:
				AlertDialog.Builder builder = new AlertDialog.Builder(this);
				builder.setMessage(R.string.confirm_close);
				builder.setPositiveButton(R.string.close_app, new DialogInterface.OnClickListener()
				{
					@Override
					public void onClick(DialogInterface dialog, int which)
					{
						// Shut down application

						// Perform logout
						mService.logout();

						// Unbind service
						Outbox.this.unbindService(mConnection);

						// make sure onStop() does not try to unbind the service
						// again
						mIsServiceBound = false;

						// Stop service
						getApplicationContext().stopService(new Intent(Outbox.this, StrokeService.class));

						// signals the activity to finish. on pause, on stop and
						// on destroy will get called after this.
						finish();
					}
				});
				builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener()
				{
					@Override
					public void onClick(DialogInterface dialog, int which)
					{
						dialog.dismiss();
					}
				});
				builder.create().show();
			break;
		}

		return true;
	}

	// Entry point for new video rating lifecycle
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data)
	{
		super.onActivityResult(requestCode, resultCode, data);

		switch (resultCode)
		{
			case (RESULT_OK):
			{
				Bundle bundle = data.getExtras();
				mVideoId = (UUID) bundle.get("de.fhkl.whatsstroke.VideoId");
				mVideoPath = (String) bundle.get("de.fhkl.whatsstroke.VideoPath");

				mService.resetTransmissionState();
				Editor editor = mPreferences.edit();
				editor.putString(VIDEO_ID, mVideoId.toString());
				editor.putString(VIDEO_PATH, mVideoPath);
				editor.putInt(STATE, TransmissionState.SEND_VIDEO.ordinal());
				editor.commit();

				restoreView();

				sendVideo();
			}
			break;

			case (RESULT_CANCELED):
			{
				mService.resetTransmissionState();

				break;
			}
		}
		// call to onResume() immediately after this method finished
	}

	/**
	 * Restores or updates the view from the data stored in the shared
	 * preferences.
	 */
	private void restoreView()
	{
		String strId = mPreferences.getString(VIDEO_ID, "");
		mVideoId = (strId == "") ? null : UUID.fromString(strId);
		mVideoPath = mPreferences.getString(VIDEO_PATH, null);

		int idleState = TransmissionState.IDLE.ordinal();

		switch (TransmissionState.values()[mPreferences.getInt(STATE, idleState)])
		{
		// nothing to display, only offering option to record a new video
			case IDLE:
				mRatingLayout.setVisibility(View.INVISIBLE);
			break;

			// in the progress of sending a video
			case SEND_VIDEO:
				mTransmissionDescription.setText(R.string.video_transmission);
				mRatingDescription.setText(mPreferences.getString(DESCRIPTION, ""));

				mProgressBar.setMax((int) mPreferences.getLong(FILE_SIZE, 0));
				mProgressBar.setProgress((int) mPreferences.getLong(PROGRESS, 0));

				mTransmissionDescription.setVisibility(View.VISIBLE);
				mProgressBar.setVisibility(View.VISIBLE);
				mRatingDescription.setVisibility(View.GONE);
				mRatingText.setVisibility(View.GONE);
				mAcceptButton.setVisibility(View.GONE);
				mCancelButton.setVisibility(View.VISIBLE);

				mRatingLayout.setVisibility(View.VISIBLE);
			break;

			// video was sent completely, wait for rating
			case WAIT_FOR_RATING:
				mTransmissionDescription.setText(R.string.video_transmission);
				mRatingDescription.setText(mPreferences.getString(DESCRIPTION, ""));
				mRatingText.setText(mPreferences.getString(RATING, ""));

				mProgressBar.setMax((int) mPreferences.getLong(FILE_SIZE, 0));
				mProgressBar.setProgress((int) mPreferences.getLong(PROGRESS, 0));

				mTransmissionDescription.setVisibility(View.VISIBLE);
				mProgressBar.setVisibility(View.VISIBLE);
				mRatingDescription.setVisibility(View.VISIBLE);
				mRatingText.setVisibility(View.VISIBLE);
				mCancelButton.setVisibility(View.GONE);

				if (mRatingText.getText() != "")
				{
					mAcceptButton.setVisibility(View.VISIBLE);
				}
				else
				{
					mAcceptButton.setVisibility(View.GONE);
				}

				mRatingLayout.setVisibility(View.VISIBLE);
			break;
		}
	}

	/**
	 * Sends the given file as byte array to the server
	 */
	private void sendVideo()
	{
		File file = new File(mVideoPath);
		long fileSize = file.length();

		mProgressBar.setMax((int) fileSize);
		Log.i("Transmission", "file size: " + fileSize);

		mService.sendVideo(mVideoId, file);
	}

	/**
	 * Safe connection to the underlying service over a returned weak reference.
	 * Like many callbacks from the system, the methods on this class are called
	 * from the main thread of your process.
	 */
	public class StrokeServiceConnection implements ServiceConnection
	{

		@Override
		public void onServiceConnected(ComponentName name, IBinder binder)
		{
			if (!mIsServiceBound)
			{
				mService = ((StrokeBinder) binder).getService();
				mIsServiceBound = true;

				Log.i("StrokeServiceConnection", "Service " + mService);
			}
		}

		@Override
		public void onServiceDisconnected(ComponentName name)
		{
			mIsServiceBound = false;
		}
	}

	/**
	 * Broadcast receiver to receive and react to broadcast messages from the
	 * underlying service.
	 */
	public class StrokeBroadcastReceiver extends BroadcastReceiver
	{
		@Override
		public void onReceive(Context context, Intent intent)
		{
			String action = intent.getAction();

			if (action.equals(Broadcast.VIDEO_TRANSMISSION_PROGRESS))
			{
				Bundle bundle = intent.getExtras();
				Integer bytesRead = (Integer) bundle.get("de.fhkl.whatsstroke.VideoTransmissionProgress");

				mProgressBar.incrementProgressBy(bytesRead);
			}
			else if (action.equals(Broadcast.UPDATE_VIEW))
			{
				restoreView();
			}
		}
	}

	/**
	 * Starts a new recording.
	 */
	private class RecordButtonListener implements OnClickListener
	{
		@Override
		public void onClick(View view)
		{
			Intent intent = new Intent(Outbox.this, Record.class);
			Outbox.this.startActivityForResult(intent, 0);
		}
	}

	/**
	 * Cancels the transmission of this video.
	 */
	private class CancelButtonListener implements OnClickListener
	{
		@Override
		public void onClick(View view)
		{
			mService.cancelVideoTransmission();

			restoreView();
		}
	}

	/**
	 * Accepts the rating received from a doctor to finish the video rating
	 * process.
	 */
	private class AcceptRatingButtonListener implements OnClickListener
	{
		@Override
		public void onClick(View view)
		{
			mService.acceptVideoRating(mVideoId);

			mVideoId = null;
			mVideoPath = null;

			restoreView();
		}
	}
}
